/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.nus.dataaccess.dao.base.dao;

import com.nus.dataaccess.dao.base.BaseEntity;
import com.nus.dataaccess.dao.util.HibernateUtil;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Order;

/**
 *
 * @author robin.foe
 */
public abstract class BaseDao<T extends BaseEntity> {

    /**
     * Return a new Session object that must be closed when the work has been
     * completed.
     *
     * @return the active Session
     */
    protected Session getSession() throws HibernateException {
         return createSession();
//        return HibernateUtil.getCurrentSession();
    }

    /**
     * Return a new Session object that must be closed when the work has been
     * completed.
     *
     * @return the active Session
     */
    public static Session createSession() throws HibernateException {
        return HibernateUtil.getCurrentSession();
    }

    /**
     * Return the specific Object class that will be used for class-specific
     * implementation of this DAO.
     *
     * @return the reference Class
     */
    protected abstract Class getReferenceClass();

    /**
     * Return all objects related to the implementation of this DAO with filter.
     * <b>Manipulate using Criteria directly to improve efficiency</b> TODO ::
     * NEED TO IMPLEMENT THE METHOD
     */
    public java.util.List<T> findAll() throws HibernateException {
        try {
           return  this.getSession().createQuery(" SELECT e FROM "+this.getReferenceClass().getName()+" e").list();
        } finally {
        }
    }

    /**
     * Obtain an instance of Query for a named query string defined in the
     * mapping file.
     *
     * @param name the name of a query defined externally
     * @return Query
     */
    @SuppressWarnings("rawtypes")
    public java.util.List getNamedQuery(String name) throws HibernateException {
        try {
            return getNamedQuery(name, getSession());
        } finally {
        }
    }

    /**
     * Obtain an instance of Query for a named query string defined in the
     * mapping file. Use the session given.
     *
     * @param name the name of a query defined externally
     * @param s the Session
     * @return Query
     */
    public java.util.List getNamedQuery(String name, Session s) throws HibernateException {
        Query q = s.getNamedQuery(name);

        return q.list();
    }

    /**
     * Obtain an instance of Query for a named query string defined in the
     * mapping file. Use the parameters given.
     *
     * @param name the name of a query defined externally
     * @param params the parameter array
     * @return Query
     */
    public java.util.List<T> getNamedQuery(String name, Serializable[] params)
            throws HibernateException {
        try {
            return getNamedQuery(name, params, getSession());
        } finally {
        }
    }

    /**
     * Obtain an instance of Query for a named query string defined in the
     * mapping file. Use the parameters given and the Session given.
     *
     * @param name the name of a query defined externally
     * @param params the parameter array
     * @s the Session
     * @return Query
     */
    public java.util.List<T> getNamedQuery(String name, Serializable[] params, Session s) throws HibernateException {
        Query q = s.getNamedQuery(name);

        if (null != params) {
            for (int i = 0; i < params.length; i++) {
                setParameterValue(q, i, params[i]);
            }
        }

        return q.list();
    }

    /**
     * Obtain an instance of Query for a named query string defined in the
     * mapping file. Use the parameters given.
     *
     * @param name the name of a query defined externally
     * @param params the parameter Map
     * @return Query
     */
    public java.util.List<T> getNamedQuery(String name, Map params) throws HibernateException {
        try {
            return getNamedQuery(name, params, getSession());
        } finally {
        }
    }

    /**
     * Obtain an instance of Query for a named query string defined in the
     * mapping file. Use the parameters given and the Session given.
     *
     * @param name the name of a query defined externally
     * @param params the parameter Map
     * @s the Session
     * @return Query
     */
    public java.util.List<T> getNamedQuery(String name, Map params, Session s) throws HibernateException {
        Query q = s.getNamedQuery(name);

        if (null != params) {
            for (Iterator i = params.entrySet().iterator(); i.hasNext();) {
                Map.Entry entry = (Map.Entry) i.next();
                setParameterValue(q, (String) entry.getKey(), entry.getValue());
            }
        }

        return q.list();
    }

    /**
     * Return a Criteria object that relates to the DAO's table. A session will
     * be created if an open one is not located. This session must be closed!
     */
    protected Criteria createCriteria() throws HibernateException {
        Session s = getSession();

        return createCriteria(s);
    }

    /**
     * Return a Criteria object that relates to the DAO's table
     */
    protected Criteria createCriteria(Session s) throws HibernateException {
        return s.createCriteria(getReferenceClass());
    }

    /**
     * Return the property of the class you would like to use for default
     * ordering
     *
     * @return the property name
     */
    public String getDefaultOrderProperty() {
        return null;
    }

    /**
     * Convenience method to set paramers in the query given based on the actual
     * object type in passed in as the value. You may need to add more
     * functionaly to this as desired (or not use this at all).
     *
     * @param query the Query to set
     * @param position the ordinal position of the current parameter within the
     * query
     * @param value the object to set as the parameter
     */
    protected void setParameterValue(Query query, int position, Object value) {
        if (null == value) {
            return;
        } else if (value instanceof Boolean) {
            query.setBoolean(position, ((Boolean) value).booleanValue());
        } else if (value instanceof String) {
            query.setString(position, (String) value);
        } else if (value instanceof Integer) {
            query.setInteger(position, ((Integer) value).intValue());
        } else if (value instanceof Long) {
            query.setLong(position, ((Long) value).longValue());
        } else if (value instanceof Float) {
            query.setFloat(position, ((Float) value).floatValue());
        } else if (value instanceof Double) {
            query.setDouble(position, ((Double) value).doubleValue());
        } else if (value instanceof BigDecimal) {
            query.setBigDecimal(position, (BigDecimal) value);
        } else if (value instanceof Byte) {
            query.setByte(position, ((Byte) value).byteValue());
        } else if (value instanceof Calendar) {
            query.setCalendar(position, (Calendar) value);
        } else if (value instanceof Character) {
            query.setCharacter(position, ((Character) value).charValue());
        } else if (value instanceof Timestamp) {
            query.setTimestamp(position, (Timestamp) value);
        } else if (value instanceof Date) {
            query.setDate(position, (Date) value);
        } else if (value instanceof Short) {
            query.setShort(position, ((Short) value).shortValue());
        }
    }

    /**
     * Convenience method to set paramers in the query given based on the actual
     * object type in passed in as the value. You may need to add more
     * functionaly to this as desired (or not use this at all).
     *
     * @param query the Query to set
     * @param key the key name
     * @param value the object to set as the parameter
     */
    protected void setParameterValue(Query query, String key, Object value) {
        if (null == key || null == value) {
            return;
        } else if (value instanceof Boolean) {
            query.setBoolean(key, ((Boolean) value).booleanValue());
        } else if (value instanceof String) {
            query.setString(key, (String) value);
        } else if (value instanceof Integer) {
            query.setInteger(key, ((Integer) value).intValue());
        } else if (value instanceof Long) {
            query.setLong(key, ((Long) value).longValue());
        } else if (value instanceof Float) {
            query.setFloat(key, ((Float) value).floatValue());
        } else if (value instanceof Double) {
            query.setDouble(key, ((Double) value).doubleValue());
        } else if (value instanceof BigDecimal) {
            query.setBigDecimal(key, (BigDecimal) value);
        } else if (value instanceof Byte) {
            query.setByte(key, ((Byte) value).byteValue());
        } else if (value instanceof Calendar) {
            query.setCalendar(key, (Calendar) value);
        } else if (value instanceof Character) {
            query.setCharacter(key, ((Character) value).charValue());
        } else if (value instanceof Timestamp) {
            query.setTimestamp(key, (Timestamp) value);
        } else if (value instanceof Date) {
            query.setDate(key, (Date) value);
        } else if (value instanceof Short) {
            query.setShort(key, ((Short) value).shortValue());
        } else if (value instanceof List) {
            query.setParameterList(key, (List) value);
        }
    }

    /**
     * Used by the base DAO classes but here for your modification Load object
     * matching the given key and return it.
     */
    protected Object load(Class refClass, Serializable key) throws HibernateException {
        try {
            return load(refClass, key, getSession());
        } finally {
        }
    }

    /**
     * Used by the base DAO classes but here for your modification Load object
     * matching the given key and return it.
     */
    public Object load(Class refClass, Serializable key, Session s) throws HibernateException {
        return s.load(refClass, key);
    }

    /**
     * Used by the base DAO classes but here for your modification Persist the
     * given transient instance, first assigning a generated identifier. (Or
     * using the current value of the identifier property if the assigned
     * generator is used.)
     */
    public Serializable save(Object obj) throws HibernateException {
        Session s = null;

        try {
            s = getSession();
            // t = beginTransaction(s);
            Serializable rtn = save(obj, s);
            // commitTransaction(t);
            return rtn;
        } catch (HibernateException e) {
            throw e;
        } finally {
        }
    }

    /**
     * Used by the base DAO classes but here for your modification Persist the
     * given transient instance, first assigning a generated identifier. (Or
     * using the current value of the identifier property if the assigned
     * generator is used.)
     */
    protected Serializable save(Object obj, Session s) throws HibernateException {
        return s.save(obj);
    }

    /**
     * Used by the base DAO classes but here for your modification Either save()
     * or update() the given instance, depending upon the value of its
     * identifier property.
     */
    public void saveOrUpdate(Object obj) throws HibernateException {
        Session s = null;

        try {
            s = getSession();
            // t = beginTransaction(s);
            saveOrUpdate(obj, s);
            // commitTransaction(t);
        } catch (HibernateException e) {
            throw e;
        } finally {
        }
    }

    /**
     * Used by the base DAO classes but here for your modification Either save()
     * or update() the given instance, depending upon the value of its
     * identifier property.
     */
    protected void saveOrUpdate(Object obj, Session s) throws HibernateException {
        s.saveOrUpdate(obj);
    }

    /**
     * Used by the base DAO classes but here for your modification Update the
     * persistent state associated with the given identifier. An exception is
     * thrown if there is a persistent instance with the same identifier in the
     * current session.
     *
     * @param obj a transient instance containing updated state
     */
    protected void update(Object obj) throws HibernateException {
        try {
            // t = beginTransaction(s);
            update(obj, getSession());
            // commitTransaction(t);
        } catch (HibernateException e) {
            throw e;
        } finally {
        }
    }

    /**
     * Used by the base DAO classes but here for your modification Update the
     * persistent state associated with the given identifier. An exception is
     * thrown if there is a persistent instance with the same identifier in the
     * current session.
     *
     * @param obj a transient instance containing updated state
     * @param s the Session
     */
    protected void update(Object obj, Session s) throws HibernateException {
        s.update(obj);
    }

    /**
     * Used by the base DAO classes but here for your modification Remove a
     * persistent instance from the datastore. The argument may be an instance
     * associated with the receiving Session or a transient instance with an
     * identifier associated with existing persistent state.
     */
    public void delete(Object obj) throws HibernateException {
        try {
            // t = beginTransaction(s);
            delete(obj, getSession());
            // commitTransaction(t);
        } catch (HibernateException e) {
            throw e;
        } finally {
        }
    }

    /**
     * Used by the base DAO classes but here for your modification Remove a
     * persistent instance from the datastore. The argument may be an instance
     * associated with the receiving Session or a transient instance with an
     * identifier associated with existing persistent state.
     */
    protected void delete(Object obj, Session s) throws HibernateException {
        s.delete(obj);
    }

    /**
     * Used by the base DAO classes but here for your modification Re-read the
     * state of the given instance from the underlying database. It is
     * inadvisable to use this to implement long-running sessions that span many
     * business tasks. This method is, however, useful in certain special
     * circumstances.
     */
    protected void refresh(Object obj, Session s) throws HibernateException {
        s.refresh(obj);
    }
    
    public void refresh(Object obj) throws HibernateException {
         try {
            // t = beginTransaction(s);
            refresh(obj, getSession());
            // commitTransaction(t);
        } catch (HibernateException e) {
            throw e;
        } finally {
        }
    }
    /**
     * Parse the associate class. eg. Category.cgname -> Category
     *
     * @param associatedPath
     * @return
     */
    protected String getAssociatedClass(String associatedPath) {
        String string = null;

        if (associatedPath != null) {
            string = associatedPath.substring(0, associatedPath.lastIndexOf("."));
        }

        return string;
    }

    /**
     * Parse the associate field. eg. Category.cgname -> cgname
     *
     * @param associatedPath
     * @return
     */
    protected String getAssociatedField(String associatedPath) {
        String string = null;

        if (associatedPath != null) {
            string = associatedPath.substring(associatedPath.lastIndexOf(".") + 1);
        }

        return string;
    }

    /**
     * Determine whether a path is associated
     *
     * @param associatedPath
     * @return
     */
    protected Boolean isAssociated(String associatedPath) {
        Boolean associate = new Boolean(false);

        if (associatedPath != null) {
            if (associatedPath.indexOf(".") > 0) {
                associate = new Boolean(true);
            }
        }

        return associate;
    }

    /**
     * Used by the base DAO classes but here for your modification Get object
     * matching the given key and return it.
     */
    protected Object get(Class refClass, Serializable key) throws HibernateException {
        Session s = null;
        try {
            s = getSession();
            return get(refClass, key, s);
        } finally {
        }
    }

    /**
     * Used by the base DAO classes but here for your modification Get object
     * matching the given key and return it.
     */
    protected Object get(Class refClass, Serializable key, Session s) throws HibernateException {
        return s.get(refClass, key);
    }
    
    public T get(java.lang.Long key) throws org.hibernate.HibernateException {
        return (T) get(getReferenceClass(), key);   
    }
}
